#!/usr/bin/python
# -*- coding: UTF-8 -*-

# MIT License
#
# Copyright (c) 2021 cloudguan rcloudguan@163.com
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

import os
import util
import subprocess
import shutil
import platform
import re
import json
from driver import Initializer
from driver import Builder


def get_build_idl_rpc():
    errcode = os.system("go get -u gitee.com/CloudGuan/rpc-go-backend/idlrpc")
    if errcode != 0:
        print("update rpc-go-backend error ")
        return False

    out_bytes = subprocess.check_output(['go', 'env', 'GOPATH'])
    gopath = out_bytes.encode('utf-8')
    if gopath[-1] == '\n':
        gopath = gopath[0:-1]

    toolname = ""
    if platform.system() == "Windows":
        toolname = "goidltool.exe"
    else:
        toolname = "goidltool"


    # 获取编译 goimport tool
    if os.path.exists('%s/' % gopath):
        errcode = os.system("go get golang.org/x/tools/cmd/goimports")
        if errcode != 0:
            print("get goimport tools error !!!!")
            return False

        errcode = os.system("go install golang.org/x/tools/cmd/goimports")
        if errcode != 0:
            print("install go import tool error !!!!")
            return False

    # 编译go idl gen tool
    errcode = os.system("go install gitee.com/CloudGuan/rpc-go-backend/idlrpc/goidltool")
    if errcode != 0:
        print("build goidltool error !!!")
        return False

    # 当前的工作路径
    working_path = os.getcwd()
    # 拷贝 goidltool.exe 相关代码
    if not os.path.exists(working_path + '/bin'):
        os.mkdir('bin')
    if not os.path.exists(working_path + '/bin/' + toolname):
        shutil.copyfile(gopath + '/bin/' + toolname, working_path + '/bin/' + toolname)

    # 拷贝依赖 建立指定目录
    if not os.path.exists("src"):
        os.mkdir('src')

    os.chdir('src')
    if not os.path.exists('go_service'):
        os.mkdir('go_service')

    # 拷贝pb生成工具
    if not os.path.exists('./go_service/rpc-go-backend'):
        os.chdir('./go_service')
        errcode = os.system("git clone https://gitee.com/CloudGuan/rpc-go-backend.git")
        if errcode != 0:
            print("[idltool] clone repo 错误！")
            return False
        os.chdir('../')
    else:
        os.chdir('./go_service/rpc-go-backend')
        if os.system("git pull") != 0:
            return False
        os.chdir("../../")
    shutil.copyfile('./go_service/rpc-go-backend/idlrpc/goidltool/golang_pb_layer.py', '../bin/golang_pb_layer.py')
    os.chdir('../')
    return True


# @title 递归处理idl的import问题
# @return 返回idl依赖列表 没有import关键字 则不处理
recom = re.compile(r'#import "([0-9a-zA-Z\_][0-9a-zA-Z\_\.\\/]*)"')  # 正则对应处理对应import


# @title 处理idl依赖关系
# @detail 这里约定最外层的文件夹不进行拷贝，但是idl子文件夹的引用关系要保持，因为import实现语义的问题
# 其实这里有个思考我是否真的需要repo具备add这个语义？
def deal_idl_import(filename):
    basedir = os.path.dirname(filename)  # 基础路径不纳入拷贝
    basename = os.path.basename(filename)  # 基础路径的子路径纳入拷贝

    if basedir != "" and basedir != ".":
        os.chdir(basedir)

    idl_list = anlays_import("", basename)

    if basedir != "" and basedir != ".":
        os.chdir("..")

    return idl_list


# @title 递归函数，返回设置了调用关系的子路径
def anlays_import(dir, basename):
    if dir and dir != "" and dir != ".":
        basename = dir + '/' + basename

    fhandle = open(basename)
    if not fhandle:
        print("can't not open idl file: " + basename)
        return

    idl_list = []
    for line in fhandle:
        line = line.lstrip()
        # search for starting with #import
        if not line or line == "":
            continue

        if line[0] == "#":
            matchres = recom.match(line)
            if not matchres:
                continue
            cdir = os.path.dirname(matchres.groups()[0])
            cname = os.path.basename(matchres.groups()[0])

            if dir != "" and dir != ".":
                idl_list.append(dir + "/" + matchres.groups()[0])
            else:
                idl_list.append(matchres.groups()[0])

            searchdir = ""
            if cdir != "" and cdir != ".":
                searchdir = cdir
            if dir != "" and dir != ".":
                searchdir = dir + '/' + searchdir

            for one in anlays_import(searchdir, cname):
                if dir != "" and dir != ".":
                    idl_list.append(dir + "/" + one)
                else:
                    idl_list.append(one)

    fhandle.close()

    return idl_list


# @title go rpc 环境初始化工具
# 用于环境检查，拉去go rpc 库 以及 构建好go idl buld tool
class Initializer_go(Initializer):
    def __init__(self, options):
        self.options = options

    def check(self):
        if not os.path.exists("repo.go.init"):
            return self.init()
        else:
            return True

    def build_backend(self):
        pass

    # 初始化go 相关依赖，初始化
    def init(self):
        print("Start init go rpc idl environment")
        # 检查基类相关环境
        if not Initializer.checkPrecondition(self):
            return False
        # 检查git 环境
        if not util.check_git_env():
            return False

        # 检查pb 环境
        if not util.check_pb_env():
            return False

        # 检查go相关环境
        if not util.check_golang_env():
            return False

        # 检查go 包体依赖 rpc 基础库 构建库
        if not get_build_idl_rpc():
            return

        # 检查rpc-frontend环境是否齐全
        frontend = "rpc-frontend"
        if platform.system() == "Windows":
            frontend += ".exe"

        if not os.path.exists('./bin/' + frontend):
            util.check_frontend(self)

        # 检查基础路径是否存在
        if not os.path.exists('src'):
            os.mkdir("src")

        if not os.path.exists("src/idl"):
            os.mkdir("src/idl")

        print ("Finish init go rpc idl environment")
        open("repo.go.init", "w+").close()
        return True

    # 环境检查
    def checkEnv(self):
        os.system("protoc --version")
        os.system("git --version")
        os.system("go version")


# @title go idl rpc 构建工具
# 用于根据idl 生成go 语言相关rpc 开发的代码工程
class Builder_go(Builder):
    def __index__(self, options):
        self.options = options

    def deleteIdl(self, name):
        pass

    def updateIdl(self, name, service_name=None):
        idl_name = self.check_idl_name(name)

    def build_idl(self, name, sname=None):
        pass

    def addIdl2Repo(self, file_name, sname=None, add=True):
        # 处理idl 路径问题
        if not add:
            file_name = "src/idl/" + file_name
        file_name = self.check_idl_name(file_name)

        if not os.path.exists(file_name):
            print("file: " + file_name + " not found")
            return False

        # 递归处理idl import 依赖文件
        idl_list = deal_idl_import(file_name)

        # 递归拷贝所有idl依赖文件
        # 拷贝idl并生成对应的文件和文件夹 src/idl 目录下 保持原有目录一致
        if add:
            util.copy_dep_idl(os.path.dirname(file_name), idl_list, 'src/idl')
            shutil.copyfile(file_name, './src/idl/' + os.path.basename(file_name))

        os.chdir('src/idl')
        # 生成对应的json
        cmd = subprocess.Popen(["../../bin/rpc-frontend", "-t", "go", "-f", os.path.basename(file_name)],
                               stdout=subprocess.PIPE,
                               stderr=subprocess.PIPE)
        ret = cmd.communicate()
        if cmd.returncode != 0:
            print(ret[1])
            return False
        # 生成对应的json
        cmd = subprocess.Popen(["../../bin/rpc-frontend", "-t", "protobuf", "-f", os.path.basename(file_name)],
                               stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        ret = cmd.communicate()
        if cmd.returncode != 0:
            print(ret[1])
            return False

        # 生成go得proto 文件
        cmd = subprocess.Popen(["python", "../../bin/golang_pb_layer.py", os.path.basename(file_name) + ".go.json",
                                os.path.basename(file_name) + ".protobuf.json"])
        ret = cmd.communicate()
        if cmd.returncode != 0:
            print(ret[1])
            return False

        # 运行go版本代码生成服务
        defsrv = ""
        if sname is not None:
            defsrv=sname

        cmd = subprocess.Popen(
            ["../../bin/goidltool", "-out", "../go_service", "-I", os.path.basename(file_name) + ".go.json", "-idlpath",
             ".", "-service", defsrv], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        ret = cmd.communicate()

        # 检查目标目录是否存在
        if not os.path.exists("../../usr"):
            os.mkdir("../../usr")

        if not os.path.exists("../../usr/go"):
            os.mkdir("../../usr/go")

        (base_name, _) = os.path.splitext(os.path.basename(file_name))

        if not os.path.exists('../../usr/go/%s'% base_name):
            os.mkdir('../../usr/go/%s' % base_name)


        # 如果只更新某个服务只更新原本服务的json, 拷贝idl里面的service usr impl 到 指定目录
        json_str = json.load(open(base_name + ".idl.go.json"))
        for service in json_str["services"]:
            if sname is not None:
                # 服务名字不应该大小写相关
                if sname.lower() != service["name"].lower():
                    continue
            # 如果目录不存在 则说明之前生成失败了
            if not os.path.exists('../go_service/%s/%s/usr' % (base_name, service["name"].lower())):
                print('generate idl %s service %s error' % (base_name, service["name"].lower()))
                continue

            # 目录存在 拷贝到 ../../usr/go/idlname/service_impl
            src = '../go_service/%s/%s/usr' % (base_name, service["name"].lower())
            dis = '../../usr/go/%s/%s_impl' % (base_name, service["name"].lower())
            # 如果目录存在 说明用户已经写过自己得 impl 了 这里不要生成 把别人写好得文件覆盖了
            if os.path.exists(dis):
                # 严谨一点，这里文件夹不为空就不拷贝了
                if not os.listdir(dis):
                    shutil.rmtree(dis)
                else:
                    continue
                
            shutil.copytree(src,dis)

            #清理对应的目录
            shutil.rmtree(src)

        os.remove('./%s.idl.go.json' % base_name)
        os.remove('./%s.idl.protobuf.json' % base_name)
        os.remove('./%s.service.proto' % base_name)
        os.chdir('../../')
        return True

    def updateRoot(self):
        pass
